﻿/*
创建日期:	2017/5/11 23:54:20     
作	  者:	张存
邮 	  箱:	zhangcunliang@126.com
描	述：
    存储过程返回结果
记	录：
    2023.3.2 增加动态类型(dynamic)的返回值
*/
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Dynamic;
using System.Linq;
using System.Text;
using ASmile.ORM.ADO;
using ASmile.ORM.Entitys;

namespace ASmile.ORM.Cores
{
    public class ProcResultBase : BaseResult
    {
        protected internal ProcResultBase(IDBHelper dbHelper, string procName, Dictionary<string, object> args = null)
        {
            DbHelper = dbHelper;
            ProcName = procName;
            OtherArgs = args;
        }

        protected string ProcName { get; }

        public DbParameter[] ProcParams { get; protected set; }
        /// <summary>
        /// ProcModel 之外的其它输入参数
        /// </summary>
        public Dictionary<string, object> OtherArgs { get; }
        /// <summary>
        /// 返回动态输出的结果
        /// </summary>
        public dynamic OutObject { get; private set; }

        protected IDBHelper DbHelper { get; }

        protected virtual void SetProcParams()
        {
            if (ProcParams != null) return;
            ProcParams = DbHelper.ConvertDbParameter(OtherArgs);
        }

        protected virtual void SetProcParams(object outObject)
        {
            if (ProcParams != null) return;
            List<DbParameter> paramList = new List<DbParameter>();
            if (OtherArgs != null)
            {
                foreach (var item in OtherArgs)
                {
                    paramList.Add(DbHelper.CreateParameter(item.Key, item.Value));
                }
            }

            if (outObject != null)
            {
                var outProInfos = Reflection.GetPropertyInfo(outObject);
                foreach (var pro in outProInfos)
                {
                    var o = pro.GetValue(outObject, null);
                    var dbParam = DbHelper.CreateParameter(pro.Name, o);
                    dbParam.Direction = ParameterDirection.Output;
                    dbParam.Size = 1024;
                    paramList.Add(dbParam);
                }
            }
            ProcParams = paramList.ToArray();
        }

        protected virtual void SetProcOutParams(object outObject)
        {
            IDictionary<string, object> expandoObject = new ExpandoObject();
            if (outObject == null) return;
            var outProInfos = Reflection.GetPropertyInfo(outObject);
            foreach (var pro in outProInfos)
            {
                var dbParam = ProcParams.FirstOrDefault(s => s.ParameterName == $"{DbHelper.ParamSign}{pro.Name}");

                if (dbParam == null) continue;
                object pValue = dbParam.Value;
                if (pValue == null || pValue == DBNull.Value) continue;

                if (pValue.GetType() != pro.PropertyType)
                {
                    var targetType = Nullable.GetUnderlyingType(pro.PropertyType);
                    if (targetType == null)
                    {
                        targetType = pro.PropertyType;
                    }
                    if (targetType.Name.Equals("Guid", StringComparison.CurrentCultureIgnoreCase))
                    {
                        pValue = new Guid(pValue.ToString());
                    }
                    else
                    {
                        pValue = Convert.ChangeType(pValue.ToString(), targetType);
                    }
                }
                if (pro.CanWrite)
                {
                    pro.SetValue(outObject, pValue, null);
                }
                expandoObject[pro.Name] = pValue;
            }

            OutObject = expandoObject;
        }

        public override string SqlContent
        {
            get
            {
                if (_SqlContent == null)
                {
                    StringBuilder sb = new StringBuilder();
                    sb.AppendFormat("{0}\r\n", ProcName);
                    if (ProcParams != null)
                    {
                        foreach (var param in ProcParams)
                        {
                            sb.AppendFormat("Param（{2}）：{0}，Value：{1}\r\n", param.ParameterName, param.Value, param.Direction);
                        }
                    }
                    _SqlContent = sb.ToString();
                }
                return _SqlContent;
            }
        }

        public virtual int ExecProc(object outModel)
        {
            SetProcParams(outModel);
            RowCount = DbHelper.ExecNonQuery(ProcName, CommandType.StoredProcedure, ProcParams);
            SetProcOutParams(outModel);
            SetIntervalResult();
            return RowCount;
        }
        /// <summary>
        /// 执行存储过程返回所影响行数
        /// </summary>
        public virtual int ExecProc()
        {
            return ExecProc(null);
        }
        /// <summary>
        /// 执行存储过程返回DataTalbe
        /// </summary>
        public virtual DataTable ToDataTable(object outModel)
        {
            SetProcParams();
            var r = DbHelper.GetDataTable(ProcName, CommandType.StoredProcedure, ProcParams);
            SetProcOutParams(outModel);
            SetIntervalResult();
            return r;
        }
        /// <summary>
        /// 执行存储过程返回DataTalbe
        /// </summary>
        public virtual DataTable ToDataTable()
        {
            return ToDataTable(null);
        }
        /// <summary>
        /// 执行存储过程返回 DataSet
        /// </summary>
        public virtual DataSet ToDataSet(object outModel)
        {
            SetProcParams();
            var r = DbHelper.GetDataSet(ProcName, CommandType.StoredProcedure, ProcParams);
            SetProcOutParams(outModel);
            SetIntervalResult();
            return r;
        }
        /// <summary>
        /// 执行存储过程返回 DataSet
        /// </summary>
        public virtual DataSet ToDataSet()
        {
            return ToDataSet(null);
        }
        /// <summary>
        /// 执行存储过程返回List
        /// </summary>
        protected virtual List<TEntity> ToList<TEntity>(bool isFirst, object outModel) where TEntity : class, new()
        {
            SetProcParams();
            var dbReader = DbHelper.GetDataReader(ProcName, CommandType.StoredProcedure, ProcParams);
            var rList = Reflection.GetListByDataReader<TEntity>(dbReader, isFirst);
            SetProcOutParams(outModel);
            SetIntervalResult();
            return rList;
        }

        protected virtual List<TEntity> ToList<TEntity>(bool isFirst) where TEntity : class, new()
        {
            return ToList<TEntity>(isFirst, null);
        }
        /// <summary>
        /// 执行存储过程返回List
        /// </summary>
        public List<TEntity> ToList<TEntity>() where TEntity : class, new()
        {
            return ToList<TEntity>(false);
        }
        /// <summary>
        /// 执行存储过程返回List
        /// </summary>
        public List<TEntity> ToList<TEntity>(object outModel) where TEntity : class, new()
        {
            return ToList<TEntity>(false, outModel);
        }
        /// <summary>
        /// 执行存储过程返回首行
        /// </summary>
        public TEntity ToEntity<TEntity>(object outModel) where TEntity : class, new()
        {
            List<TEntity> rList = ToList<TEntity>(true, outModel);
            if (rList != null && rList.Count > 0)
            {
                return rList[0];
            }
            else
            {
                return null;
            }
        }

        public TEntity ToEntity<TEntity>() where TEntity : class, new()
        {
            return ToEntity<TEntity>(null);
        }
        /// <summary>
        /// 返回首行首列
        /// </summary>
        public object ToObject(object outModel)
        {
            SetProcParams();
            object obj = DbHelper.GetScalar(ProcName, CommandType.StoredProcedure, ProcParams);
            RowCount = (obj == null || obj is DBNull) ? 0 : 1;
            SetProcOutParams(outModel);
            SetIntervalResult();
            return obj;
        }

        public object ToObject()
        {
            return ToObject(null);
        }
        /// <summary>
        /// 返回首行首列
        /// </summary>
        public T ToObject<T>(object outModel)
        {
            object obj = ToObject(outModel);
            if (obj == null || obj is DBNull)
            {
                return default;
            }
            object result = Convert.ChangeType(obj, typeof(T));
            return (T)result;
        }

        public T ToObject<T>()
        {
            return ToObject<T>(null);
        }

        protected internal override bool SetTiming(DateTime startTime, Action<BaseResult> argTimingEndHandle)
        {
            return SetTimingHandle(startTime, argTimingEndHandle);
        }
    }

    public class ProcResult : ProcResultBase
    {
        protected internal ProcResult(IDBHelper dbHelper, ProcEntityBase procModel, Dictionary<string, object> otherArgs = null)
            : base(dbHelper, procModel.GetProcedureName(), otherArgs)
        {
            ProcModel = procModel;
        }
        /// <summary>
        /// 存储过程参数对象
        /// </summary>
        ProcEntityBase ProcModel { get; }

        protected override void SetProcParams()
        {
            if (ProcParams != null) return;
            ProcParams = ProcModel.GetProcParamObj(DbHelper.CreateDbParameter, OtherArgs);
        }
        /// <summary>
        /// 执行存储过程返回所影响行数
        /// </summary>
        public override int ExecProc()
        {
            SetProcParams();
            RowCount = DbHelper.ExecNonQuery(ProcName, CommandType.StoredProcedure, ProcParams);
            ProcModel.SetOutParamValue(ProcParams);
            SetIntervalResult();
            return RowCount;
        }

        /// <summary>
        /// 执行存储过程返回DataTalbe
        /// </summary>
        public override DataTable ToDataTable()
        {
            SetProcParams();
            var r = DbHelper.GetDataTable(ProcName, CommandType.StoredProcedure, ProcParams);
            ProcModel.SetOutParamValue(ProcParams);
            SetIntervalResult();
            return r;
        }
        /// <summary>
        /// 执行存储过程返回 DataSet
        /// </summary>
        public override DataSet ToDataSet()
        {
            SetProcParams();
            var r = DbHelper.GetDataSet(ProcName, CommandType.StoredProcedure, ProcParams);
            ProcModel.SetOutParamValue(ProcParams);
            SetIntervalResult();
            return r;
        }
        /// <summary>
        /// 执行存储过程返回List
        /// </summary>
        protected override List<TEntity> ToList<TEntity>(bool isFirst)
        {
            SetProcParams();
            var dbReader = DbHelper.GetDataReader(ProcName, CommandType.StoredProcedure, ProcParams);
            var rList = Reflection.GetListByDataReader<TEntity>(dbReader, isFirst);
            ProcModel.SetOutParamValue(ProcParams);
            SetIntervalResult();
            return rList;
        }
    }
}